package apigateway.project.core;

import apigateway.project.commen.config.Rule;
import apigateway.project.commen.config.ServiceDefinition;
import apigateway.project.commen.config.ServiceInstance;
import apigateway.project.commen.utils.Netutils;
import apigateway.project.configcenter.Impl.NacosConfigCenterImpl;
import apigateway.project.configcenter.NacosConfigCenter;
import apigateway.project.configcenter.RulesChangeListener;
import apigateway.project.register.Impl.NacosRegisterCenter;
import apigateway.project.register.Interface.RegisterCenter;
import apigateway.project.register.Interface.RegisterCenterListener;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * 启动类
 */
@Slf4j
public class BootStrap {
    public static void main(String[] args) {
        //加载网关配置
        Config config = ConfigLoader.getInstance().loadConfig(args);
        System.out.println(config.getPort());

        //初始化配置中心
        NacosConfigCenter configCenter = new NacosConfigCenterImpl();
        configCenter.init(config.getAddressOfNacos(),config.getDataId(),config.getGroupId());
        configCenter.pullConfigAndListener(rules -> {
            CacheConfig.getCacheConfig().cache(rules);
            initFLow(rules);
        });

        //启动容器
        new Container(config).start();

        //注册中心
        final RegisterCenter registerCenter = registerAndSubscribe(config);
    }
    private static RegisterCenter registerAndSubscribe(Config config) {
        final RegisterCenter registerCenter = new NacosRegisterCenter();
        //初始化nacos注册中心
        registerCenter.init(config.getAddressOfNacos(), config.getGroupId());
        //构造网关服务定义和实例
        ServiceInstance serviceInstance = makeServiceInstance(config);
        ServiceDefinition serviceDefinition = makeServiceDefinition(config);

        //服务注册
        registerCenter.register(serviceDefinition, serviceInstance);
        //订阅服务
        registerCenter.subscribe(new RegisterCenterListener() {

            @Override
            public void onVarify(ServiceDefinition serviceDefinition, Set<ServiceInstance> serviceInstances) {
                log.info("service and instance : {},{}", serviceDefinition.getServiceName(), JSON.toJSON(serviceInstances));
                CacheConfig instance = CacheConfig.getCacheConfig();
                //更新服务列表
                instance.putServiceDefinition(serviceDefinition.getServiceName(),serviceDefinition);
                //添加服务实例到服务中
                instance.putServiceInstances(serviceDefinition.getServiceName(),serviceInstances);

            }
        });
       return registerCenter;
    };
    private static ServiceInstance makeServiceInstance(Config config){
        ServiceInstance serviceInstance = new ServiceInstance();
        String ip = Netutils.getLocalIp();
        serviceInstance.setIp(ip);
        int port = config.getPort();
        serviceInstance.setPort(port);
        serviceInstance.setInstanceId(serviceInstance.getIp()+":"+port);
        serviceInstance.setRegisterTime(System.currentTimeMillis());
        return serviceInstance;
    };
    private static ServiceDefinition makeServiceDefinition(Config config){
        ServiceDefinition serviceDefinition = new ServiceDefinition();
        serviceDefinition.setServiceName(config.getApplicationName());
        serviceDefinition.setEnvType(config.getGroupId());

        return serviceDefinition;
    }

    /**
     * 初始化sentinel流控的资源以及流控方式
     * @param rules
     */
    private static void initFLow(List<Rule> rules) {
        List<FlowRule> flowRules = FlowRuleManager.getRules();
        if(flowRules==null){
            flowRules = new ArrayList<>();
        }
        for (Rule rule : rules) {
            FlowRule flowRule = new FlowRule();
            flowRule.setResource(rule.getServiceName());
            if("qps".equalsIgnoreCase(rule.getFlowConfig().getType())){
                flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
            } else if ("thread".equalsIgnoreCase(rule.getFlowConfig().getType())) {
                flowRule.setGrade(RuleConstant.FLOW_GRADE_THREAD);
            }
            if("default".equalsIgnoreCase(rule.getFlowConfig().getStrategy())){
                flowRule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
            }else if("queue".equalsIgnoreCase(rule.getFlowConfig().getStrategy())){
                flowRule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER);
            }else if("warmup".equalsIgnoreCase(rule.getFlowConfig().getStrategy())){
                flowRule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_WARM_UP);
            }
            flowRule.setCount(rule.getFlowConfig().getCount());
            flowRules.add(flowRule);
        }
        FlowRuleManager.loadRules(flowRules);
    }

}
